/*
 * Copyright 2011-2012 by The Regents of the University of California Licensed
 * under the Apache License, Version 2.0 (the "License"); you may not use this
 * file except in compliance with the License. you may obtain a copy of the
 * License from
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package edu.ucsd.db.bassarisk.query.path;

import edu.ucsd.db.bassarisk.exception.SchemaValidationException;
import edu.ucsd.db.bassarisk.exception.UnexpectedCaseException;
import edu.ucsd.db.bassarisk.exception.VisitorException;
import edu.ucsd.db.bassarisk.query.ValidationTraversalLog;
import edu.ucsd.db.bassarisk.query.path.DSDElements.DSDAggregate;
import edu.ucsd.db.bassarisk.query.path.DSDElements.DSDContext;
import edu.ucsd.db.bassarisk.query.path.DSDElements.DSDDescent;
import edu.ucsd.db.bassarisk.query.path.DSDElements.DSDFilter;
import edu.ucsd.db.bassarisk.schema.DataSequenceClass;
import edu.ucsd.db.bassarisk.schema.Schema;
import edu.ucsd.db.bassarisk.schema.Schema.SequenceRelationship;
import edu.ucsd.db.bassarisk.treepattern.DescentAxis;

public class DSDSchemaInitializeVisitor implements
    DSDElementVisitor<DataSequenceClass, DataSequenceClass> {
  private final ValidationTraversalLog log;
  private final Schema schema;

  public DSDSchemaInitializeVisitor(final Schema schema,
                                 final ValidationTraversalLog log) {
    this.schema = schema;
    this.log = log;
  }

  @Override
  public DataSequenceClass visitAggregate(final DSDAggregate elem,
                                          final DataSequenceClass parentSeqClass)
      throws VisitorException {
    return this.visitDescent(elem, parentSeqClass);
  }

  @Override
  public DataSequenceClass visitContext(final DSDContext elem,
                                        final DataSequenceClass parentSeqClass)
      throws VisitorException {
    if (elem.hasNext()) {
      return elem.getNext().accept(this, parentSeqClass);
    } else {
      return parentSeqClass;
    }
  }

  @Override
  public DataSequenceClass visitDescent(final DSDDescent elem,
                                        final DataSequenceClass parentSeqClass)
      throws VisitorException {
    DataSequenceClass seqClass = elem.getSequenceClass();
    try {
      if (!seqClass.isInitialized()) {
        seqClass.initailize(this.schema);
      }
      SequenceRelationship rela =
          this.schema.findSeqRelationship(seqClass.getName(),
                                          parentSeqClass.getName());
      switch (rela) {
      case AllContainedBy:
        if (elem.getAxis() == DescentAxis.Child) {
          seqClass.refineToChildrenOf(parentSeqClass);
        }
        break;
      case Equals:
      case ContainsAll:
      case ContainsSome:
      case SomeContainedBy:
        if (elem.getAxis() == DescentAxis.Child) {
          seqClass.refineToChildrenOf(parentSeqClass);
        } else {
          seqClass.refineToDescendantsOf(parentSeqClass);
        }
        break;
      case Excludes:
      case ContainsNone:
      case NoneContainedBy:
        String msg = "Path violates schema constraints. All solutions are empty.";
        // TODO log.addWarning(new SchemaValidationException(msg));
        this.log.addError(new SchemaValidationException(msg));
      case Orthogonal:
        break;
      default:
        throw UnexpectedCaseException.ofEnum(SequenceRelationship.class);
      }
    } catch (SchemaValidationException e) {
      this.log.addError(e);
    }
    if (elem.hasNext()) {
      elem.getNext().accept(this, seqClass);
    }
    return seqClass;
  }

  @Override
  public DataSequenceClass visitFilter(final DSDFilter elem,
                                       final DataSequenceClass parentSeqClass) {
    return parentSeqClass;
  }
}
